﻿using System.Collections.Generic;
using System.Linq;

namespace DB.SchemaSync.Library.Models
{
	public class Table : DbObject
	{
        /// <summary>
        /// 模式名
        /// </summary>
		public string Schema { get; set; }

        /// <summary>
        /// 表明
        /// </summary>
		public string Name { get; set; }

        /// <summary>
        /// 主键列
        /// </summary>
		public string IdentityColumn { get; set; }

        /// <summary>
        /// 聚簇索引
        /// </summary>
		public string ClusteredIndex { get; set; }

		/// <summary>
		/// 表当前数据行数
		/// </summary>
		public int RowCount { get; set; }

		/// <summary>
		/// If true, then INSERT statements are generated from the source data when script is generated
		/// </summary>
		public bool MigrateData { get; set; }

		/// <summary>
		/// Criteria that filters data to migrate
		/// </summary>
		public string MigrateDataWhere { get; set; }

		/// <summary>
		/// If empty, then it's okay to drop and rebuild table
		/// </summary>
		public bool IsEmpty { get { return RowCount == 0; } }

        /// <summary>
        /// 表格列
        /// </summary>
		public IEnumerable<Column> Columns { get; set; } = Enumerable.Empty<Column>();

        /// <summary>
        /// 索引
        /// </summary>
		public IEnumerable<Index> Indexes { get; set; } = Enumerable.Empty<Index>();

        /// <summary>
        /// 触发器
        /// </summary>
        public IEnumerable<Trigger> Triggers { get; set; } = Enumerable.Empty<Trigger>();

        public override IEnumerable<string> CreateCommands(SqlSyntax syntax)
		{
			string columns = string.Join(",\r\n", Columns.OrderBy(col => col.InternalId).Select(col => $"\t{col.Definition(syntax)}"));
			yield return $"CREATE TABLE <{ToString()}> (\r\n{columns}\r\n)";

			foreach (var index in Indexes)
			{
			    foreach (var cmd in index.CreateCommands(syntax))
			    {
			        yield return cmd;
			    }
			}
		}

		public override IEnumerable<string> DropCommands(SqlSyntax syntax)
		{
			yield return $"DROP TABLE <{ToString()}>";
		}

		public override bool Equals(object obj)
		{
			Table tbl = obj as Table;
			if (tbl != null)
			{
				return (Schema ?? string.Empty).ToLower().Equals(tbl.Schema.ToLower()) && (Name ?? string.Empty).ToLower().Equals(tbl.Name.ToLower());
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (Schema ?? string.Empty).ToLower().GetHashCode() + (Name ?? string.Empty).ToLower().GetHashCode();
		}

		public override string ToString()
		{
			return (!string.IsNullOrEmpty(Schema)) ? $"{Schema}.{Name}" : Name;
		}

		public override bool IsAltered(DbObject compare)
		{
			throw new System.NotImplementedException();
		}

		public override IEnumerable<DbObject> GetDependencies(Database database)
		{
			return database.ForeignKeys.Where(fk => fk.ReferencedTable.Equals(this) && !fk.ReferencingTable.Equals(this));
		}

		public IEnumerable<ForeignKey> GetForeignKeys(Database database)
		{
			return database.ForeignKeys.Where(fk => fk.ReferencingTable.Equals(this));
		}

		public override ExcludeObject GetExcludeObject()
		{
			return new ExcludeObject() { Type = "Table", Name = $"{Schema}.{Name}" };
		}

		private IEnumerable<Column> GetIndexColumnsOfType(IndexType indexType)
		{
			return GetIndexColumns().Where(xcol => xcol.Index.Type == indexType).Select(xcol => xcol.Column);
		}

		private IEnumerable<IndexColumnInfo> GetIndexColumns()
		{
			// thanks to https://stackoverflow.com/a/7611501/2023653
			var indexColumns = Indexes.SelectMany(ndx => ndx.Columns, (ndx, col) => new { Index = ndx, ColumnName = col.Name });

			return from col in Columns
				   join xcol in indexColumns on col.Name.ToLower() equals xcol.ColumnName.ToLower()
				   select new IndexColumnInfo() { Column = col, Index = xcol.Index };
		}
	}
}